SBT 构建定义 (Build Definition)
两类 Build Definition风格:
- 新:多工程 .sbt 构建定义
- 旧:bare .sbt 构建定义
为了指定构建定义的风格,以便用不同风格的构建定义完成相同的项目,可以在基础目录建立 project/build.properties文件,包含下列内容:
sbt.version=1.0.2
注意这里等号两边没有空格
如果当前操作系统环境中没有包含指定版本(风格)的sbt工具,系统会自动下载安装。
Play 2.6 用的SBT版本是 0.13.15,依旧是旧风格。
1. bare .sbt 风格的构建定义
包含了一个完整的 Setting[_],而不区分project.
2. 多工程 .sbt 风格的构建定义
构建定义内容存储在 build.sbt 文件中,它定义了当前目录的工程(subproject)定义,例如:
lazy val root = (project in file("."))
.settings(
name := "Hello",
scalaVersion := "2.12.3"
)
每一个工程对应一个不可变的映射表(immutable map)(一些键值对的集合)来描述工程。
一个构建定义是一个Project(英文文档中称为 subproject,以防止歧义),拥有一个类型为 Setting[T] 的列表,Setting[T] 是会影响到 sbt 保存键值对的 map 的一种转换,T 是每一个 value 的类型。
每个键值对利用 build.sbt DSL 语言来进行定义,如下图
其中 key 是 SBT SettingKey[T], TaskKey[T], or InputKey[T] 的一个实例。因此,如果对应的value类型不为T,则编译会报错
build.sbt中也可以包含其它 val , lazy val 和 def 对象。
- SettingKey : 在加载该项目(subproject)时会被计算,此后保持不变
- TaskKey : 一个key对应一个称为 task 的value,会被重复计算
- InputKey : 对应task的输入参数,详细的关于 task 和 input 的有这个文档
上述三类是Key的类型,其定义来源则有两类: built-in keys (定义在 sbt.Keys._) 和 custom keys。
自定义的key可以如下列方式定义在 build.sbt文件中。如下例,定义了一个名为 hello的新TaskKey:
lazy val hello = taskKey[Unit]("An example task")
关于Task
TaskKey[T] 是用来定义 task 的。Tasks 就是像 compile 或者 package 这样的操作。它们可能返回 Unit(Unit 在 Scala 中表示 void),或者可能返回 task 相关的返回值, 例如 package 就是一个类型为 TaskKey[File] 的 task, 它的返回值是其生成的 jar 文件。
对于每个 subproject,都定义了它的一系列基本设定和任务。
基本设定为 SettingKey,类似不可更改的全局常量
而任务则用来执行这个project的不同分支功能(计算任务),任务本身由TaskKey定义,任务的输入则由InputKey来定义
Task中保存了计算的代码,例如:
lazy val hello = taskKey[Unit]("An example task")
lazy val root = (project in file("."))
.settings(
hello := { println("Hello!") }
)
添加依赖库
有两种方式添加第三方的依赖。一种是将 jar 文件 放入 lib/
(非托管的依赖)中,另一种是在 build.sbt
中添加托管的依赖
后者的示例如下(添加版本为 10.4.1.3 的 Apache Derby 库作为依赖):
val derby = "org.apache.derby" % "derby" % "10.4.1.3"
lazy val commonSettings = Seq(
organization := "com.example",
version := "0.1.0",
scalaVersion := "2.12.3"
)
lazy val root = (project in file("."))
.settings(
commonSettings,
name := "hello",
libraryDependencies += derby
)
需要注意的是这里 Key libraryDependencies的两个特殊用法,一个是 +=,一个是 %。
Scope
scope 用于定义Key的作用域(上下文 context)
scope轴:就是指哪个维度来划分不同的作用域,共有三种类型:
- projects
- configurations
- tasks
类似于RGB的原色划分,从不同轴上对作用域进行划分、组合后,就可以成为明确的Key作用域了,
因此,一个完整的sbt作用域实际上是一个三元组,包含:subproject, configuration, task,例如:
scalaOptions in (projA, Compile, console)
1. subproject scope axis
可以在一个 single build中包含若干个subprojects, 用来指定每个subproject中独立的Key设定
也可以将设定指定在 ThisBuild,即整个build共享
2. configuration scop axis
configuration scope 常用于包含项目的依赖关系,包括:its own classpath, sources, generated packages, etc.
常见的configuration包括:
Compile
which defines the main build (src/main/scala
).Test
which defines how to build tests (src/test/scala
).Runtime
which defines the classpath for therun
task.
configuration中也可以包含继承关系,如下图
3. task scop axis
不是非常能理解,特别是关于一个task key 可以用于另一个key的作用域的问题。
在进行scope设置时,每个轴都可以用与该轴类型一致的实例代替,(例如 task 轴可以用一个 task 代替),或者该轴可以被特定的值Global
代替。
如果一个 task 轴的值是 Global,那么该 setting 的值将被应用到所有的 task 上。
查看 Scope 中 key
$ sbt
> inspect name
...
[info] Provided by:
[info] {file:/Users/jiamin/Applications/helloSBT/}root/*:name
...
可以看到,上述这个名为 name 的Key,其scope定义的结构为:
{<build-uri>}<project-id>/config:intask::key
{<build-uri>}/<project-id>
标识 project 轴。如果 project 轴有构建全局 scope,将没有<project-id>
部分。{file:/Users/jiamin/Applications/helloSBT/}root
- config 标识 configuration 轴。
*
代表全局 - intask 标识 task 轴。没有显示出来 也代表全局
- key 标识 scope 下的 key。
可以这样定义不同轴下的Key值
lazy val root = (project in file("."))
.settings(
name := "helloSBT",
version := "1.0",
scalaVersion := "2.11.4"
)
organization := name.value
name in Compile := "hello_in_compile"
name in packageBin := "hello in task"
大概能够理解Key的作用,也能理解不同scope下指定相同Key不同值的作用。 但是具体的设置、查看方法,以及在具体使用过程中,应该如何活用这类机制,还不是很清楚。